import os
import sys
import asyncio
import subprocess
from src.ui.i18n import _
from PySide6.QtWidgets import (
    QWidget, QVBoxLayout, QFormLayout, QHBoxLayout,
    QPushButton, QFileDialog, QLineEdit, QComboBox, QGroupBox,
    QMessageBox, QPlainTextEdit, QProgressBar, QLabel
)
from PySide6.QtCore import QThread, Signal, Qt, QTimer
import tempfile

from src.utils.process import run_command

from src.config import app_config
from src.processing.dubbing_worker import DubbingWorker
from src.translation.edge_tts import EdgeTTS
from src.utils.logger import get_logger




class DubbingTab(QWidget):
    class AudioPlayer(QThread):
        def __init__(self, file_path, logger, parent=None):
            super().__init__(parent)
            self.file_path = file_path
            self.logger = logger

        def run(self):
            try:
                # Use QSoundEffect from PySide6 instead of simpleaudio
                from PySide6.QtMultimedia import QSoundEffect
                from PySide6.QtCore import QUrl
                import time
                
                # Create a QSoundEffect in the main thread context
                # Since we can't do this directly in a QThread, we'll just play the file
                # using a system command as a workaround
                import subprocess
                if os.path.exists(self.file_path):
                    # Use system default audio player
                    if os.name == 'nt':  # Windows
                        os.startfile(self.file_path)
                    elif sys.platform == "darwin":  # macOS
                        subprocess.run(["afplay", self.file_path], check=True)
                    else:  # Linux
                        subprocess.run(["aplay", self.file_path], check=True)
            except Exception as e:
                self.logger.error(f"Failed to play audio: {e}")
            finally:
                try:
                    if os.path.exists(self.file_path):
                        os.remove(self.file_path)
                except OSError as e:
                    self.logger.error(
                        f"Failed to remove temp audio file: {e}")

    def __init__(self, main_window=None, parent=None):
        super().__init__(parent)
        self.main_window = main_window
        self.worker = None
        self.voices = []
        self.logger = get_logger(__name__)
        self.audio_player = None
        self.elapsed_time = 0
        self.timer = QTimer(self)

        self._setup_ui()
        self._connect_signals()
        self._load_voices()
        self.retranslate_ui()

    def _setup_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(10, 15, 10, 10)
        main_layout.setSpacing(15)

        # --- Settings Group ---
        settings_group = QGroupBox(_("Dubbing Settings"))
        form_layout = QFormLayout(settings_group)
        form_layout.setSpacing(10)

        self.srt_path_edit = QLineEdit()
        self.srt_path_button = QPushButton()

        srt_file_layout = QHBoxLayout()
        srt_file_layout.addWidget(self.srt_path_edit)
        srt_file_layout.addWidget(self.srt_path_button)

        self.tts_service_combo = QComboBox()
        self.tts_service_combo.addItems([EdgeTTS.get_name()])

        self.voice_combo = QComboBox()
        self.listen_button = QPushButton()
        self.listen_button.setEnabled(False)

        voice_layout = QHBoxLayout()
        voice_layout.addWidget(self.voice_combo, 1)  # Add stretch factor
        voice_layout.addWidget(self.listen_button)

        self.srt_path_label = QLabel()
        self.tts_service_label = QLabel()
        self.voice_label = QLabel()

        form_layout.addRow(self.srt_path_label, srt_file_layout)
        form_layout.addRow(self.tts_service_label, self.tts_service_combo)
        form_layout.addRow(self.voice_label, voice_layout)

        main_layout.addWidget(settings_group)

        self.log_edit = QPlainTextEdit()
        self.log_edit.setReadOnly(True)
        main_layout.addWidget(self.log_edit)

        # --- Progress and Status ---
        progress_layout = QHBoxLayout()
        self.status_label = QLabel()
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        progress_layout.addWidget(self.status_label, 1)
        progress_layout.addWidget(self.progress_bar, 2)
        main_layout.addLayout(progress_layout)

        # --- Timing and Action Buttons ---
        self.time_label = QLabel()
        self.time_label.setAlignment(
            Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        action_layout = QHBoxLayout()
        action_layout.addWidget(self.time_label)
        action_layout.addStretch()
        self.dub_button = QPushButton()
        self.stop_button = QPushButton()
        self.stop_button.setEnabled(False)
        self.stop_button.setStyleSheet("background-color: #bf616a;")
        action_layout.addWidget(self.stop_button)
        action_layout.addWidget(self.dub_button)
        main_layout.addLayout(action_layout)

    def _connect_signals(self):
        self.srt_path_button.clicked.connect(self.select_srt_file)
        self.dub_button.clicked.connect(self.start_dubbing)
        self.stop_button.clicked.connect(self.stop_dubbing)
        self.listen_button.clicked.connect(self.listen_to_voice)
        self.timer.timeout.connect(self._update_timer_display)

    def retranslate_ui(self):
        self.srt_path_button.setText(_("Select SRT File..."))
        self.listen_button.setText(_("Listen"))
        self.status_label.setText(_("Idle"))
        self.time_label.setText(_("Time: --:--:--"))
        self.dub_button.setText(_("Start Dubbing"))
        self.stop_button.setText(_("Stop"))
        self.srt_path_label.setText(_("Subtitle File:"))
        self.tts_service_label.setText(_("TTS Service:"))
        self.voice_label.setText(_("Select Voice:"))
        self.voice_combo.setToolTip(_("Loading voices..."))

    def _load_voices(self):
        """Asynchronously loads the voice list in a separate thread to avoid UI blocking."""
        self.voice_combo.setEnabled(False)
        self.voice_combo.addItem(_("Loading voice list..."))

        class VoiceLoader(QThread):
            finished = Signal(list)

            def run(self):
                try:
                    loop = asyncio.get_running_loop()
                except RuntimeError:
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)

                voices = loop.run_until_complete(EdgeTTS.get_voices())
                self.finished.emit(voices)

        self.voice_loader = VoiceLoader()
        self.voice_loader.finished.connect(self.on_voices_loaded)
        self.voice_loader.start()

    def on_voices_loaded(self, voices):
        self.voices = voices
        self.voice_combo.clear()

        for voice in self.voices:
            display_name = f"{voice['Locale']} - {voice['Gender']} - {voice['ShortName']}"
            self.voice_combo.addItem(
                display_name, userData=voice)  # Store full voice dict

        self.voice_combo.setEnabled(True)
        self.listen_button.setEnabled(True)
        self.voice_combo.setToolTip("")

        # After loading, try to prioritize based on the current SRT file
        self._prioritize_voice_by_srt_path()

    def _prioritize_voice_by_srt_path(self):
        srt_path = self.srt_path_edit.text()
        if not srt_path:
            self._reprioritize_voices("zh-CN")  # Default to Chinese
            return

        # Extract language code from filename like "video.en.srt"
        base_name = os.path.basename(srt_path)
        parts = base_name.split('.')
        lang_code = "zh-CN"  # Default
        if len(parts) > 2:
            # Assuming the language code is the second to last part
            potential_lang = parts[-2]
            # A simple check to see if it resembles a language code (e.g., 'en', 'ja')
            if len(potential_lang) == 2 or (len(potential_lang) == 5 and '-' in potential_lang):
                # We need to find a matching locale in our voice list.
                # For example, 'en' should match 'en-US'.
                for voice in self.voices:
                    if voice['Locale'].lower().startswith(potential_lang.lower()):
                        lang_code = voice['Locale']
                        break

        self._reprioritize_voices(lang_code)

    def _reprioritize_voices(self, preferred_locale):
        """Sorts the voice_combo list to prioritize a specific locale."""
        if not self.voices:
            return

        self.voice_combo.blockSignals(True)

        # Sort voices: preferred locale first, then by locale, then by short name
        sorted_voices = sorted(self.voices, key=lambda v: (
            v['Locale'].lower() != preferred_locale.lower(),
            v['Locale'],
            v['ShortName']
        ))

        self.voice_combo.clear()
        for voice in sorted_voices:
            display_name = f"{voice['Locale']} - {voice['Gender']} - {voice['ShortName']}"
            self.voice_combo.addItem(display_name, userData=voice)

        self.voice_combo.setCurrentIndex(0)
        self.voice_combo.blockSignals(False)

    def select_srt_file(self):
        last_dir = app_config.get("paths.last_srt_dir", os.path.expanduser("~"))
        file_path, _filter = QFileDialog.getOpenFileName(
            self, _("Select SRT file for dubbing"), last_dir, _(
                "SRT Files (*.srt)")
        )
        if file_path:
            self.srt_path_edit.setText(file_path)
            app_config.set("paths.last_srt_dir", os.path.dirname(file_path), emit_signal=False)
            app_config.save(emit_signal=False)
            self._prioritize_voice_by_srt_path()

    def start_dubbing(self):
        srt_path = self.srt_path_edit.text()
        if not srt_path or not os.path.exists(srt_path):
            QMessageBox.warning(self, _("Warning"), _(
                "Please select a valid SRT file."))
            return
        if self.voice_combo.count() == 0 or not self.voice_combo.isEnabled():
            QMessageBox.warning(self, _("Warning"), _(
                "Please wait for the voice list to load and select a voice."))
            return

        self.dub_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.log_edit.clear()
        self.append_log(_("Starting dubbing task..."))
        self.time_label.setText(_("Time: 00:00:00"))
        self.elapsed_time = 0
        self.timer.start(1000)  # Update every second

        tts_service_name = self.tts_service_combo.currentText()
        selected_index = self.voice_combo.currentIndex()
        selected_voice_data = self.voice_combo.currentData()
        selected_voice_name = selected_voice_data.get(
            "Name") if selected_voice_data else None
        selected_voice_locale = selected_voice_data.get(
            "Locale") if selected_voice_data else "en-US"

        concurrent_tasks = app_config.get("dubbing.concurrent_tasks", 10)
        self.logger.info(
            f"Dubbing task started, concurrent_tasks: {concurrent_tasks}")

        self.worker = DubbingWorker(
            srt_path,
            tts_service_name,
            selected_voice_name,
            selected_voice_locale,
            concurrent_tasks=concurrent_tasks
        )
        self.worker.progress_update.connect(self.update_progress_bar)
        # Show detailed logs on the UI
        self.worker.log_message.connect(self.append_log)
        # Also send to main window status bar and log file
        self.worker.log_message.connect(self.logger.info)
        self.worker.task_finished.connect(self.on_dubbing_finished)
        self.worker.error.connect(self.on_dubbing_error)
        self.worker.finished.connect(self.on_worker_finished)
        self.worker.start()

    def update_progress_bar(self, value, total, stage):
        self.status_label.setText(stage)
        if total > 0:
            self.progress_bar.setMaximum(total)
            self.progress_bar.setValue(value)

    def _update_timer_display(self):
        self.elapsed_time += 1
        from src.utils.formatter import format_seconds
        self.time_label.setText(_("Time: {}").format(
            format_seconds(self.elapsed_time)))

    def append_log(self, message):
        """Appends a message to the log text edit and scrolls to the bottom."""
        self.log_edit.appendPlainText(message)
        self.log_edit.verticalScrollBar().setValue(
            self.log_edit.verticalScrollBar().maximum())

    def on_dubbing_finished(self, message, output_path):
        self.append_log(f"\n🎉 {message}")
        self.append_log(_("Output file saved at: {}").format(output_path))
        QMessageBox.information(self, _("Success"), _(
            "{}\nOutput file: {}").format(message, output_path))

    def on_dubbing_error(self, error_message):
        self.append_log(f"\n❌ " + _("Error: {}").format(error_message))
        if _("Task cancelled") not in error_message:
            QMessageBox.critical(self, _("Dubbing Failed"), error_message)

    def on_worker_finished(self, success, total_time):
        self.timer.stop()
        from src.utils.formatter import format_seconds
        self.dub_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.progress_bar.setVisible(False)
        self.time_label.setText(
            _("Total Time: {}").format(format_seconds(total_time)))
        if success:
            self.status_label.setText(_("Completed"))
        else:
            self.status_label.setText(_("Failed or Cancelled"))

    def stop_dubbing(self):
        if self.worker and self.worker.isRunning():
            self.status_label.setText(_("Stopping..."))
            self.worker.stop()
            self.timer.stop()
            self.stop_button.setEnabled(False)

    def listen_to_voice(self):
        if self.voice_combo.count() == 0 or not self.voice_combo.isEnabled():
            return

        selected_voice_data = self.voice_combo.currentData()
        if not selected_voice_data:
            return
        selected_voice = selected_voice_data.get("Name")
        current_locale = selected_voice_data.get("Locale", "")
        if 'en' in current_locale.lower():
            sample_text = _("Hello, welcome to this application.")
        else:
            sample_text = _("Voice, giving new soul to video.")

        self.listen_button.setEnabled(False)
        self.listen_button.setText(_("Generating..."))

        class ListenWorker(QThread):
            finished = Signal(str, str)  # error_message, file_path

            def __init__(self, parent=None):
                super().__init__(parent)
                self.logger = get_logger(__name__)

            def run(self):
                # Safely run asyncio code within a QThread
                try:
                    loop = asyncio.get_running_loop()
                except RuntimeError:  # 'RuntimeError: There is no current event loop...'
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)

                try:
                    tts_service = EdgeTTS()
                    # Create temporary files with correct extensions
                    with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as mp3_tmpfile:
                        mp3_tmp_path = mp3_tmpfile.name
                    
                    with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as wav_tmpfile:
                        wav_tmp_path = wav_tmpfile.name

                    # Generate the audio with Edge TTS (outputs MP3)
                    loop.run_until_complete(tts_service.save(
                        sample_text, mp3_tmp_path, selected_voice))
                    
                    # Check if the MP3 file was created and has content
                    if not os.path.exists(mp3_tmp_path) or os.path.getsize(mp3_tmp_path) == 0:
                        raise ValueError("No audio was received. Please verify that your parameters are correct.")

                    # Use ffmpeg to convert mp3 to wav for simpleaudio
                    command = [
                        "ffmpeg", "-i", mp3_tmp_path,
                        "-acodec", "pcm_s16le",  # PCM signed 16-bit little-endian
                        "-ac", "1",             # Mono
                        "-ar", "16000",         # 16kHz sample rate
                        "-y", wav_tmp_path
                    ]
                    run_command(command, log_callback=self.logger.debug)
                    
                    # Check if the WAV file was created and has content
                    if not os.path.exists(wav_tmp_path) or os.path.getsize(wav_tmp_path) == 0:
                        raise ValueError("Failed to convert audio to WAV format.")

                    # Clean up the temporary mp3 file
                    os.remove(mp3_tmp_path)

                    self.finished.emit(None, wav_tmp_path)
                except Exception as e:
                    # Clean up any temporary files that might have been created
                    try:
                        if 'mp3_tmp_path' in locals() and os.path.exists(mp3_tmp_path):
                            os.remove(mp3_tmp_path)
                        if 'wav_tmp_path' in locals() and os.path.exists(wav_tmp_path):
                            os.remove(wav_tmp_path)
                    except:
                        pass
                    self.finished.emit(str(e), None)

        self.listen_worker = ListenWorker()
        self.listen_worker.finished.connect(self.on_listen_finished)
        self.listen_worker.start()

    def on_listen_finished(self, error, file_path):
        self.listen_button.setEnabled(True)
        self.listen_button.setText(_("Listen"))

        if error:
            QMessageBox.warning(self, _("Listen Failed"), _(
                "Failed to generate listen audio: {}").format(error))
            if file_path and os.path.exists(file_path):
                os.remove(file_path)
            return

        if not file_path:
            return

        # Play audio using system audio player (non-blocking)
        try:
            if os.path.exists(file_path):
                # Use system default audio player in a non-blocking way
                if os.name == 'nt':  # Windows
                    os.startfile(file_path)
                elif sys.platform == "darwin":  # macOS
                    subprocess.Popen(["afplay", file_path])
                else:  # Linux
                    subprocess.Popen(["aplay", file_path])
                
                # Schedule cleanup after a delay (assuming audio playback is short)
                from PySide6.QtCore import QTimer
                QTimer.singleShot(5000, lambda: self._cleanup_audio_file(file_path))
        except Exception as e:
            self.logger.error(f"Failed to play audio: {e}")
            QMessageBox.warning(self, _("Listen Failed"), _(
                "Failed to play audio: {}").format(str(e)))
            
    def _cleanup_audio_file(self, file_path):
        """Clean up the temporary audio file after playback."""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
        except Exception as e:
            self.logger.error(f"Failed to remove temp audio file: {e}")
